package com.hmall.pay.service.impl;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.client.TradeClient;
import com.hmall.api.client.UserClient;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.utils.BeanUtils;
import com.hmall.common.utils.UserContext;
import com.hmall.pay.domain.dto.PayApplyDTO;
import com.hmall.pay.domain.dto.PayOrderFormDTO;
import com.hmall.pay.domain.po.PayOrder;
import com.hmall.pay.enums.PayStatus;
import com.hmall.pay.mapper.PayOrderMapper;
import com.hmall.pay.service.IPayOrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
 * 支付订单服务实现类
 * 实现支付订单的创建、支付等核心业务逻辑
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {

    // 用户服务客户端（用于余额扣减）
    private final UserClient userClient;

    // 交易服务客户端（用于更新订单状态）
    private final TradeClient tradeClient;

    // RabbitMQ模板（用于发送支付成功消息）
    private final RabbitTemplate rabbitTemplate;

    /**
     * 申请支付订单核心方法
     * 包含幂等性校验、支付订单创建等逻辑
     */
    @Override
    public String applyPayOrder(PayApplyDTO applyDTO) {

        // 1. 幂等性校验（避免重复创建支付订单）
        PayOrder payOrder = checkIdempotent(applyDTO);
        // 2. 返回支付订单ID
        return payOrder.getId().toString();

    }

    /**
     * 尝试使用余额支付订单
     * 包含状态校验、余额扣减、支付状态更新等逻辑
     */
    @Override
    @Transactional
    public void tryPayOrderByBalance(PayOrderFormDTO payOrderDTO) {

        // 1. 查询支付订单
        PayOrder po = getById(payOrderDTO.getId());
        // 2. 校验支付订单状态（必须为待支付）
        if (!PayStatus.WAIT_BUYER_PAY.equalsValue(po.getStatus())) {
            throw new BizIllegalException("交易已支付或关闭！");
        }
        // 3. 调用用户服务扣减余额（支付密码验证在deductMoney中完成）
        userClient.deductMoney(payOrderDTO.getPw(), po.getAmount());
        // 4. 修改支付订单状态为支付成功
        boolean success = markPayOrderSuccess(payOrderDTO.getId(), LocalDateTime.now());
        if (!success) {
            throw new BizIllegalException("交易已支付或关闭！");
        }
        // 5. 通过消息队列通知交易服务更新订单状态（异步处理）
        try {
            rabbitTemplate.convertAndSend("pay.direct", "pay.success", po.getBizOrderNo());
        } catch (Exception e) {
            log.error("发送支付状态通知失败，订单:{}", po.getBizOrderNo(), e);
        }
    }

    /**
     * 标记支付订单为成功状态
     * 使用乐观锁确保状态更新的一致性
     */
    public boolean markPayOrderSuccess(Long id, LocalDateTime successTime) {
        return lambdaUpdate()
                .set(PayOrder::getStatus, PayStatus.TRADE_SUCCESS.getValue()) // 设置状态为支付成功
                .set(PayOrder::getPaySuccessTime, successTime) // 设置支付成功时间
                .eq(PayOrder::getId, id) // 条件：ID匹配
                // 乐观锁条件：当前状态必须为待提交或待支付（防止重复更新）
                .in(PayOrder::getStatus,
                        PayStatus.NOT_COMMIT.getValue(),
                        PayStatus.WAIT_BUYER_PAY.getValue())
                .update();
    }

    /**
     * 幂等性校验核心方法
     * 确保相同业务订单不会重复创建支付订单
     */
    private PayOrder checkIdempotent(PayApplyDTO applyDTO) {

        // 1. 根据业务订单号查询支付订单
        PayOrder oldOrder = queryByBizOrderNo(applyDTO.getBizOrderNo());

        // 2. 支付订单不存在，创建新订单
        if (oldOrder == null) {
            PayOrder payOrder = buildPayOrder(applyDTO);
            payOrder.setPayOrderNo(IdWorker.getId()); // 生成支付单号（雪花算法）
            save(payOrder);
            return payOrder;
        }

        // 3. 支付订单已存在且已支付，抛出异常
        if (PayStatus.TRADE_SUCCESS.equalsValue(oldOrder.getStatus())) {
            throw new BizIllegalException("订单已经支付！");
        }

        // 4. 支付订单已存在且已关闭，抛出异常
        if (PayStatus.TRADE_CLOSED.equalsValue(oldOrder.getStatus())) {
            throw new BizIllegalException("订单已关闭");
        }

        // 5. 支付订单已存在但支付渠道不一致，重置并更新
        if (!StringUtils.equals(oldOrder.getPayChannelCode(), applyDTO.getPayChannelCode())) {
            PayOrder payOrder = buildPayOrder(applyDTO);
            payOrder.setId(oldOrder.getId());
            payOrder.setQrCodeUrl(""); // 清空旧二维码
            updateById(payOrder);
            payOrder.setPayOrderNo(oldOrder.getPayOrderNo());
            return payOrder;
        }

        // 6. 支付订单已存在且状态正常，直接返回旧订单
        return oldOrder;
    }

    /**
     * 构建支付订单对象
     * 从DTO转换并设置默认值
     */
    private PayOrder buildPayOrder(PayApplyDTO payApplyDTO) {

        // 1. DTO转换为实体对象
        PayOrder payOrder = BeanUtils.toBean(payApplyDTO, PayOrder.class);
        // 2. 设置默认值
        payOrder.setPayOverTime(LocalDateTime.now().plusMinutes(120L)); // 支付超时时间（120分钟）
        payOrder.setStatus(PayStatus.WAIT_BUYER_PAY.getValue()); // 初始状态：待支付
        payOrder.setBizUserId(UserContext.getUser()); // 设置当前用户ID
        return payOrder;
    }

    /**
     * 根据业务订单号查询支付订单
     */
    public PayOrder queryByBizOrderNo(Long bizOrderNo) {
        return lambdaQuery()
                .eq(PayOrder::getBizOrderNo, bizOrderNo)
                .one();
    }
}